#ifndef     __AUDIO_DECODER_H
#define     __AUDIO_DECODER_H

extern "C" {
#include <libswresample/swresample.h>
}
#include "./decoder.h"
#include "../utils/frame.h"
#include "../utils/audio_buf.h"
#include "../utils/audio_param.h"

class AudioDecoder : public Decoder {

public:
    AudioDecoder(AVStream *stream) : Decoder(stream) {
        AVSampleFormat fmt;
        switch (m_ctx->sample_fmt) {
            case AV_SAMPLE_FMT_U8:
            case AV_SAMPLE_FMT_U8P:
                fmt = AV_SAMPLE_FMT_U8;
            break;

            case AV_SAMPLE_FMT_S16:
            case AV_SAMPLE_FMT_S16P:
                fmt = AV_SAMPLE_FMT_S16;
            break;

            case AV_SAMPLE_FMT_S32:
            case AV_SAMPLE_FMT_S32P:
                fmt = AV_SAMPLE_FMT_S32;
            break;

            case AV_SAMPLE_FMT_FLT:
            case AV_SAMPLE_FMT_FLTP:
                fmt = AV_SAMPLE_FMT_FLT;
            break;

            default:
                throw FFmpegException("NOT ALLOW AUDIO FMT");
            break;
        }
        int ec = swr_alloc_set_opts2(
            &m_swr_ctx,
            &m_ctx->ch_layout, fmt, m_ctx->sample_rate,
            &m_ctx->ch_layout, m_ctx->sample_fmt, m_ctx->sample_rate,
            0, nullptr
        );
        if (m_swr_ctx == nullptr) {
            throw FFmpegException("swr_alloc_set_opts is null");
        }
        ec = swr_init(m_swr_ctx);
        if (ec < 0) {
            swr_free(&m_swr_ctx);
            throw FFmpegException("swr init fail");
        }
        m_temp_frame = Frame( av_frame_alloc() );
        m_fmt = fmt;
    }

    ~AudioDecoder() {
        swr_free(&m_swr_ctx);
    }

    AudioParam param() const {
        return AudioParam(m_ctx);
    }

    bool receive_audio_frame(AudioBuf &buf) {
        bool ret = receive_frame(m_temp_frame.frame);
        if (!ret) {
            return ret;
        }
        size_t len = av_samples_get_buffer_size(
            nullptr,
            m_ctx->ch_layout.nb_channels,
            m_temp_frame->nb_samples,
            m_fmt, 0
        );
        buf = AudioBuf( len );
        int ec = swr_convert(
            m_swr_ctx,
            &buf.buf,
            buf.len,
            (const uint8_t **) m_temp_frame->data,
            m_temp_frame->nb_samples
        );
        if (ec < 0) {
            throw FFmpegException("swr convert", ec);
        }
        buf.pts = m_temp_frame->best_effort_timestamp;
        m_temp_frame.unref();
        return ret;
    }

private:
    SwrContext *m_swr_ctx = nullptr;
    Frame m_temp_frame;
    AVSampleFormat m_fmt;
};

#endif
